/*
 * (C) Copyright 2013 George Daswani (georgedaswani@gmail.com) and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *    George Daswani (georgedaswani@gmail.com)
 */

package am.api.wrapper;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.log4j.Logger;

import am.api.AMConnection;
import am.api.model.AMCredential;
import am.api.model.AMDate;
import am.api.model.AMString;
import am.api.util.NLS;
import am.internal.AMLibrary;
import am.internal.AMStaticLib;

import com.sun.jna.Native;

class AMConnectionImpl implements AMConnection {

	private final static Logger logger = Logger
			.getLogger(AMConnectionImpl.class);

	private final AMLibrary amLibrary;

	private int connHandle;

	private final AMCredential credential;

	private final Map<Integer, Boolean> handleMap;

	private final ConnectionPool pool;

	AMConnectionImpl(ConnectionPool pool, AMCredential credential) {
		super();
		amLibrary = AMStaticLib.INSTANCE;
		this.pool = pool;
		this.credential = credential;
		this.handleMap = new ConcurrentHashMap<Integer, Boolean>();
	}

	private void checkInternalState() {
		if (connHandle == 0) {
			throw new IllegalStateException(
					NLS.ERRORS.getString("connection.invalid"));
		}
	}

	private void checkQueryHandle(int queryHandle) {
		if (queryHandle == 0) {
			throw new IllegalStateException(
					NLS.ERRORS.getString("queryhandle.invalid"));
		}
	}

	@Override
	public int clearLastError() {
		checkInternalState();
		return amLibrary.AmClearLastError(connHandle);
	}

	@Override
	public void close() {

		releaseRegisteredHandles();

		if (pool != null) {
			logger.debug(String.format("Returning connection [%s] to [%s]",
					this, pool));

			pool.returnObject(credential, this);
		} else {
			reallyClose();
		}

	}

	@Override
	public int commit() {
		checkInternalState();

		int status = amLibrary.AmCommit(connHandle);

		return status;
	}

	@Override
	public int connectionName(AMString connectionName) {
		checkInternalState();

		byte[] clone = createCopy(connectionName.getBuffer());

		int status = 0;

		status = amLibrary.AmConnectionName(connHandle, clone,
				connectionName.getBufferLength());

		connectionName.setBuffer(unwrapFromNative(clone));

		return status;

	}

	@Override
	public int convertDateBasicToUnix(int tmTime) {
		checkInternalState();
		return amLibrary.AmConvertDateBasicToUnix(connHandle, tmTime);
	}

	@Override
	public int convertDateIntlToUnix(String dateAsString) {
		checkInternalState();
		return amLibrary.AmConvertDateIntlToUnix(connHandle, dateAsString);
	}

	@Override
	public int convertDateStringToUnix(String dateAsString) {
		checkInternalState();
		return amLibrary.AmConvertDateStringToUnix(connHandle, dateAsString);
	}

	@Override
	public int convertDateUnixToBasic(int dateAsUnix) {
		checkInternalState();
		return amLibrary.AmConvertDateUnixToBasic(connHandle, dateAsUnix);
	}

	@Override
	public int convertDateUnixToIntl(int unixDate, AMString dateAsIntlStr) {
		checkInternalState();

		int status = 0;

		byte[] clone = createCopy(dateAsIntlStr.getBuffer());

		status = amLibrary.AmConvertDateUnixToIntl(connHandle, unixDate, clone,
				dateAsIntlStr.getBufferLength());

		dateAsIntlStr.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int convertDateUnixToString(int dateAsUnix, AMString dateAsStr) {
		checkInternalState();

		int status = 0;

		byte[] clone = createCopy(dateAsStr.getBuffer());

		status = amLibrary.AmConvertDateUnixToString(connHandle, dateAsUnix,
				clone, dateAsStr.getBufferLength());

		dateAsStr.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int convertDoubleToString(double dSrc, AMString dblAsString) {

		int status = 0;

		byte[] clone = createCopy(dblAsString.getBuffer());

		status = amLibrary.AmConvertDoubleToString(dSrc, clone,
				dblAsString.getBufferLength());

		dblAsString.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int convertMonetaryToString(double dMonetarySrc, AMString dblAsString) {

		int status = 0;

		byte[] clone = createCopy(dblAsString.getBuffer());

		status = amLibrary.AmConvertMonetaryToString(dMonetarySrc, clone,
				dblAsString.getBufferLength());

		dblAsString.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public double convertStringToDouble(String dblAsString) {
		return amLibrary.AmConvertStringToDouble(dblAsString);
	}

	@Override
	public double convertStringToMonetary(String monetaryAsString) {
		return amLibrary.AmConvertStringToMonetary(monetaryAsString);
	}

	private byte[] createCopy(byte[] original) {
		return original.clone();
	}

	@Override
	public int createLink(int srcRecHandle, String linkName, int srcDstHandle) {
		checkInternalState();
		return amLibrary.AmCreateLink(srcRecHandle, linkName, srcDstHandle);
	}

	@Override
	public int createRecord(String tblName) {
		checkInternalState();
		return amLibrary.AmCreateRecord(connHandle, tblName);
	}

	@Override
	public int currentDate() {
		return amLibrary.AmCurrentDate();
	}

	@Override
	public int currentServerDate() {
		checkInternalState();
		return amLibrary.AmCurrentServerDate(connHandle);
	}

	@Override
	public int dateAdd(int startAsUnixDate, int duration) {
		return amLibrary.AmDateAdd(startAsUnixDate, duration);
	}

	@Override
	public int dateAddLogical(int startAsUnixDate, int duration) {
		return amLibrary.AmDateAddLogical(startAsUnixDate, duration);
	}

	@Override
	public int dateDiff(int endAsUnixDate, int startAsUnixDate) {
		return amLibrary.AmDateDiff(endAsUnixDate, startAsUnixDate);
	}

	@Override
	public int dbExecAql(String aqlQuery) {
		checkInternalState();
		return amLibrary.AmDbExecAql(connHandle, aqlQuery);
	}

	@Override
	public int dbGetDate(String aqlQuery) {
		checkInternalState();
		return amLibrary.AmDbGetDate(connHandle, aqlQuery);
	}

	@Override
	public double dbGetDouble(String aqlQuery) {
		checkInternalState();
		return amLibrary.AmDbGetDouble(connHandle, aqlQuery);
	}

	@Override
	public int dbGetLimitedList(String aqlQuery, AMString result,
			String colSeparator, String lineSeparator, String idSeparator,
			int maxSize, int errorType) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmDbGetLimitedList(connHandle, aqlQuery, clone,
				result.getBufferLength(), colSeparator, lineSeparator,
				idSeparator, maxSize, errorType);

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int dbGetList(String aqlQuery, AMString result, String colSeparator,
			String lineSeparator, String idSeparator) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmDbGetList(connHandle, aqlQuery, clone,
				result.getBufferLength(), colSeparator, lineSeparator,
				idSeparator);

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int dbGetListEx(String aqlQuery, AMString result,
			String colSeparator, String lineSeparator, String idSeparator) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmDbGetListEx(connHandle, aqlQuery, clone,
				result.getBufferLength(), colSeparator, lineSeparator,
				idSeparator);

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int dbGetLong(String aqlQuery) {
		checkInternalState();
		return amLibrary.AmDbGetLong(connHandle, aqlQuery);
	}

	@Override
	public int dbGetPk(String tableName, String whereClause) {
		checkInternalState();
		return amLibrary.AmDbGetPk(connHandle, tableName, whereClause);
	}

	@Override
	public int dbGetString(String query, AMString result, String colSeparator,
			String lineSeparator) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmDbGetString(connHandle, query, result.getBuffer(),
				result.getBufferLength(), colSeparator, lineSeparator);

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int dbGetStringEx(String query, AMString result,
			String colSeparator, String lineSeparator) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmDbGetStringEx(connHandle, query, clone,
				result.getBufferLength(), colSeparator, lineSeparator);

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int deleteLink(int srcRecHandle, String linkName, int srcDstHandle) {
		checkInternalState();
		return amLibrary.AmDeleteLink(srcRecHandle, linkName, srcDstHandle);
	}

	@Override
	public int deleteRecord(int recordHandle) {
		checkInternalState();
		return amLibrary.AmDeleteRecord(recordHandle);
	}

	@Override
	public int duplicateRecord(int recordHandle, int insert) {
		checkInternalState();
		return amLibrary.AmDuplicateRecord(recordHandle, insert);
	}

	@Override
	public int enumValList(String enumName, AMString value, int caseSensitive,
			String lineSeparator) {
		checkInternalState();

		byte[] clone = createCopy(value.getBuffer());

		int status = 0;

		status = amLibrary.AmEnumValList(connHandle, enumName, clone,
				value.getBufferLength(), caseSensitive, lineSeparator);

		value.setBuffer(unwrapFromNative(clone));

		return status;
	}

	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}
		if (obj == this) {
			return true;
		}
		if (obj.getClass() != getClass()) {
			return false;
		}
		AMConnectionImpl rhs = (AMConnectionImpl) obj;
		return new EqualsBuilder().appendSuper(super.equals(rhs))
				.append(connHandle, rhs.connHandle).isEquals();
	}

	@Override
	public int executeActionById(int actionId, String tableName, int recordId) {
		checkInternalState();
		return amLibrary.AmExecuteActionById(connHandle, actionId, tableName,
				recordId);
	}

	@Override
	public int executeActionByName(String sqlName, String tableName,
			int recordId) {
		checkInternalState();
		return amLibrary.AmExecuteActionByName(connHandle, sqlName, tableName,
				recordId);
	}

	@Override
	public int exportDocument(int documentId, String fileName) {
		checkInternalState();
		return amLibrary.AmExportDocument(connHandle, documentId, fileName);
	}

	@Override
	public int flushTransaction() {
		checkInternalState();
		return amLibrary.AmFlushTransaction(connHandle);
	}

	@Override
	public int formatCurrency(double amount, String currency, AMString result) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmFormatCurrency(amount, currency, clone,
				result.getBufferLength());

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int formatLong(int number, String format, AMString result) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmFormatLong(connHandle, number, format, clone,
				result.getBufferLength());

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getComputeString(String tableName, int recordId,
			String template, AMString result) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmGetComputeString(connHandle, tableName, recordId,
				template, clone, result.getBufferLength());

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getField(int objHandle, int position) {
		checkInternalState();
		return amLibrary.AmGetField(objHandle, position);
	}

	@Override
	public int getFieldCount(int objHandle) {
		checkInternalState();
		return amLibrary.AmGetFieldCount(objHandle);
	}

	@Override
	public int getFieldDateOnlyValue(int recHandle, int fieldPos) {
		checkInternalState();
		return amLibrary.AmGetFieldDateOnlyValue(recHandle, fieldPos);
	}

	@Override
	public int getFieldDateValue(int recHandle, int fieldPos) {
		checkInternalState();
		return amLibrary.AmGetFieldDateValue(recHandle, fieldPos);
	}

	@Override
	public int getFieldDescription(int fldHandle, AMString target) {
		checkInternalState();

		byte[] clone = createCopy(target.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldDescription(fldHandle, clone,
				target.getBufferLength());

		target.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public double getFieldDoubleValue(int objHandle, int fieldPos) {
		checkInternalState();
		return amLibrary.AmGetFieldDoubleValue(objHandle, fieldPos);
	}

	@Override
	public int getFieldFormat(int fldHandle, AMString target) {
		checkInternalState();

		byte[] clone = createCopy(target.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldFormat(fldHandle, clone,
				target.getBufferLength());

		target.setBuffer(unwrapFromNative(clone));

		return status;

	}

	@Override
	public int getFieldFormatFromName(String tblName, String fldName,
			AMString result) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldFormatFromName(connHandle, tblName,
				fldName, clone, result.getBufferLength());

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getFieldFromName(int objHandle, String fldName) {
		checkInternalState();
		return amLibrary.AmGetFieldFromName(objHandle, fldName);
	}

	@Override
	public int getFieldLabel(int fldHandle, AMString result) {
		checkInternalState();

		byte[] clone = createCopy(result.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldLabel(fldHandle, clone,
				result.getBufferLength());

		result.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getFieldLabelFromName(String tableName, String fieldName,
			AMString fieldLabel) {
		checkInternalState();

		byte[] clone = createCopy(fieldLabel.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldLabelFromName(connHandle, tableName,
				fieldName, clone, fieldLabel.getBufferLength());

		fieldLabel.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getFieldLongValue(int objHandle, int fieldPosition) {
		checkInternalState();
		return amLibrary.AmGetFieldLongValue(objHandle, fieldPosition);
	}

	@Override
	public int getFieldName(int objHandle, int fldPosition, AMString fieldName) {
		checkInternalState();

		byte[] clone = createCopy(fieldName.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldName(objHandle, fldPosition, clone,
				fieldName.getBufferLength());

		fieldName.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getFieldSize(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetFieldSize(fldHandle);
	}

	@Override
	public int getFieldSqlName(int fldHandle, AMString fieldSQLName) {
		checkInternalState();

		byte[] clone = createCopy(fieldSQLName.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldSqlName(fldHandle, clone,
				fieldSQLName.getBufferLength());

		fieldSQLName.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getFieldStrValue(int queryHandle, int position, AMString target) {
		checkInternalState();
		checkQueryHandle(queryHandle);

		byte[] clone = createCopy(target.getBuffer());

		int status = 0;

		status = amLibrary.AmGetFieldStrValue(queryHandle, position, clone,
				target.getBufferLength());

		target.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getFieldType(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetFieldType(fldHandle);
	}

	@Override
	public int getFieldUserType(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetFieldUserType(fldHandle);
	}

	@Override
	public int getRecordFromMainId(String tableName, int id) {

		checkInternalState();

		return registerHandle(amLibrary.AmGetRecordFromMainId(connHandle,
				tableName, id));

	}

	@Override
	public int getRecordHandle(int qryHandle) {

		checkInternalState();

		return registerHandle(amLibrary.AmGetRecordHandle(qryHandle));

	}

	@Override
	public int getRecordId(int recHandle) {
		checkInternalState();
		return amLibrary.AmGetRecordId(recHandle);
	}

	@Override
	public int getRelDstField(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetRelDstField(fldHandle);
	}

	@Override
	public int getRelSrcField(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetRelSrcField(fldHandle);
	}

	@Override
	public int getRelTable(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetRelSrcField(fldHandle);
	}

	@Override
	public int getReverseLink(int fldHandle) {
		checkInternalState();
		return amLibrary.AmGetReverseLink(fldHandle);
	}

	@Override
	public int getSelfFromMainId(String tableName, int recordId,
			AMString recordDescription) {
		checkInternalState();

		byte[] clone = createCopy(recordDescription.getBuffer());

		int status = 0;

		status = amLibrary.AmGetSelfFromMainId(connHandle, tableName, recordId,
				clone, recordDescription.getBufferLength());

		recordDescription.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int getVersion(AMString amVersion) {
		checkInternalState();

		byte[] clone = createCopy(amVersion.getBuffer());

		int status = 0;

		status = amLibrary.AmGetVersion(clone, amVersion.getBufferLength());

		amVersion.setBuffer(unwrapFromNative(clone));

		return status;
	}

	public int hashCode() {
		return new HashCodeBuilder(99, 97).appendSuper(super.hashCode())
				.append(connHandle).toHashCode();

	}

	@Override
	public int importDocument(int docId, String tableName, String fileName,
			String category, String designation) {
		checkInternalState();
		return amLibrary.AmImportDocument(connHandle, docId, tableName,
				fileName, category, designation);
	}

	@Override
	public int insertRecord(int recHandle) {
		checkInternalState();
		return amLibrary.AmInsertRecord(recHandle);
	}

	@Override
	public int isConnected() {
		return amLibrary.AmIsConnected(connHandle);
	}

	@Override
	public int lastError() {
		return amLibrary.AmLastError(connHandle);
	}

	@Override
	public int lastErrorMsg(AMString errorMessage) {

		byte[] clone = createCopy(errorMessage.getBuffer());

		int status = 0;

		status = amLibrary.AmLastErrorMsg(connHandle, clone,
				errorMessage.getBufferLength());

		errorMessage.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int listToString(AMString target, String source, String colSep,
			String lineSep, String idSep) {

		byte[] clone = createCopy(target.getBuffer());

		int status = 0;

		status = amLibrary.AmListToString(clone, target.getBufferLength(),
				source, colSep, lineSep, idSep);

		target.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int loginId() {
		checkInternalState();
		return amLibrary.AmLoginId(connHandle);
	}

	@Override
	public int loginName(AMString loginName) {
		checkInternalState();

		byte[] clone = createCopy(loginName.getBuffer());

		int status = 0;

		status = amLibrary.AmLoginName(connHandle, clone,
				loginName.getBufferLength());

		loginName.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int openConnection(String database, String username, String password) {

		if (connHandle != 0) {
			throw new IllegalStateException("Connection still open.");
		} else {
			connHandle = amLibrary.AmOpenConnection(database, username,
					password);
			checkInternalState();
		}

		return connHandle;
	}

	@Override
	public int purgeRecord(int recHandle) {
		checkInternalState();
		return amLibrary.AmPurgeRecord(recHandle);
	}

	@Override
	public int queryCreate() {
		checkInternalState();
		return amLibrary.AmQueryCreate(connHandle);
	}

	@Override
	public int queryExec(int queryHandle, String aqlQuery) {
		checkInternalState();
		checkQueryHandle(queryHandle);
		return amLibrary.AmQueryExec(queryHandle, aqlQuery);
	}

	@Override
	public int queryGet(int qryHandle, String aqlQuery) {
		checkInternalState();
		return amLibrary.AmQueryGet(qryHandle, aqlQuery);
	}

	@Override
	public int queryNext(int qryHandle) {
		checkInternalState();
		checkQueryHandle(qryHandle);
		return amLibrary.AmQueryNext(qryHandle);
	}

	@Override
	public int querySetAddMainField(int qryHandle, int addMainField) {
		checkInternalState();
		checkQueryHandle(qryHandle);
		return amLibrary.AmQuerySetAddMainField(qryHandle, addMainField);
	}

	@Override
	public int querySetFullMemo(int qryHandle, int fullMemo) {
		checkInternalState();
		checkQueryHandle(qryHandle);
		return amLibrary.AmQuerySetFullMemo(qryHandle, fullMemo);
	}

	@Override
	public int queryStartTable(int qryHandle) {
		checkInternalState();
		checkQueryHandle(qryHandle);
		return amLibrary.AmQueryStartTable(qryHandle);
	}

	@Override
	public int queryStop(int qryHandle) {
		checkInternalState();
		return amLibrary.AmQueryStop(qryHandle);
	}

	public void reallyClose() {

		logger.debug(String.format("Forced connection closure = [%s].", this));

		if (connHandle > 0 && 0 == amLibrary.AmCloseConnection(connHandle)) {
			connHandle = 0;
		}
	}

	@Override
	public int refreshAllCaches() {
		checkInternalState();
		return amLibrary.AmRefreshAllCaches(connHandle);
	}

	private int registerHandle(int handle) {

		if (handle > 0) {

			Integer key = new Integer(handle);

			if (false == handleMap.containsKey(new Integer(handle))) {
				handleMap.put(key, Boolean.TRUE);
			}

		}

		return handle;

	}

	@Override
	public int releaseHandle(int queryHandle) {

		checkInternalState();

		int status = amLibrary.AmReleaseHandle(queryHandle);

		if (status > 0) {
			Integer key = new Integer(status);
			handleMap.remove(key);
		}

		return status;
	}

	private void releaseRegisteredHandles() {

		if (!handleMap.isEmpty()) {

			logger.warn(NLS.MESSAGES.getString("handles.leaking"));

			for (Integer key : handleMap.keySet()) {
				releaseHandle(key);
			}

		}

	}

	@Override
	public int rollBack() {
		int status = amLibrary.AmRollback(connHandle);
		return status;
	}

	@Override
	public int setFieldDateOnlyValue(int recHandle, String fieldName,
			AMDate dateOnlyValue) {
		checkInternalState();
		return amLibrary.AmSetFieldDateOnlyValue(recHandle, fieldName,
				dateOnlyValue.getValue());
	}

	@Override
	public int setFieldDateValue(int recHandle, String fieldName,
			AMDate dateTimeValue) {
		checkInternalState();
		return amLibrary.AmSetFieldDateValue(recHandle, fieldName,
				dateTimeValue.getValue());
	}

	@Override
	public int setFieldDoubleValue(int recHandle, String fieldName, double value) {
		checkInternalState();
		return amLibrary.AmSetFieldDoubleValue(recHandle, fieldName, value);
	}

	@Override
	public int setFieldLongValue(int recHandle, String fieldName, int value) {
		checkInternalState();
		return amLibrary.AmSetFieldLongValue(recHandle, fieldName, value);
	}

	@Override
	public int setFieldStrValue(int recHandle, String fieldName, String value) {
		checkInternalState();
		return amLibrary.AmSetFieldStrValue(recHandle, fieldName, value);
	}

	@Override
	public int sqlTextConst(AMString target, String aqlQuery) {
		checkInternalState();

		byte[] clone = createCopy(target.getBuffer());

		int status = 0;

		status = amLibrary.AmSqlTextConst(clone, target.getBufferLength(),
				aqlQuery);

		target.setBuffer(unwrapFromNative(clone));

		return status;
	}

	@Override
	public int startTransaction() {

		checkInternalState();

		int status = amLibrary.AmStartTransaction(connHandle);

		return status;
	}

	public String toString() {
		return new ToStringBuilder(this).append("connHandle", connHandle)
				.toString();
	}

	private byte[] unwrapFromNative(byte[] nativeBytes) {
		return Native.toString(nativeBytes).getBytes();
	}

	@Override
	public int updateRecord(int recHandle) {
		checkInternalState();
		return amLibrary.AmUpdateRecord(recHandle);
	}

}
